Skip to content

feat(transaction-pay): add TokenPay strategy with Across provider + metrics#7806

Open
pedronfigueiredo wants to merge 2 commits intomainfrom
pnf/cor-across-strategy
Open

feat(transaction-pay): add TokenPay strategy with Across provider + metrics#7806
pedronfigueiredo wants to merge 2 commits intomainfrom
pnf/cor-across-strategy

Conversation

@pedronfigueiredo
Copy link
Contributor

@pedronfigueiredo pedronfigueiredo commented Feb 2, 2026

Explanation

This PR introduces a TokenPay strategy that routes to provider adapters (Relay + Across), adds an Across provider end‑to‑end, and tracks quote/execution latency and costs. It also enforces current Across limitations (same‑chain swaps, perps deposits, and type‑4 authorization lists) with explicit gating and TODOs.

Key changes

  • New TokenPay strategy + provider adapter interface/registry.
  • Across provider:
    • Quote requests to /swap/approval (GET or POST with actions).
    • Quote normalization for provider fees, source/target network fees, dust, amounts, and duration.
    • Actions API support for delegated calls (token transfer + delegation).
    • Submit flow for approval + swap txs, requiredTransactionIds tracking, intent completion.
  • Relay adapter:
    • Wraps existing Relay quote + submit logic for TokenPay.
  • Feature flags:
    • TokenPay provider config (Across API base/key/app fees/slippage; Relay quote URL).
  • Gating and TODOs:
    • Reject same‑chain quotes (mUSD conversions) when Across can’t do same‑chain swaps.
    • Disable perps deposits (Hypercore) until Across supports USDC‑PERPs.
    • Block type‑4/EIP‑7702 authorizationList actions until supported (first‑time Polymarket deposits).
  • Metrics:
    • Across quote latency stored on quote original.metrics.latency.
    • Execution latency stored on transactionMeta.metamaskPay.executionLatencyMs.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Adds a new cross-chain quote/execution provider and rewires strategy selection/execution paths with fallback, which can affect transaction submission behavior and metrics recording; changes are covered by extensive new tests but touch critical payment flows.

Overview
Adds a new Across pay strategy (quotes + submission) alongside Relay/Bridge, with feature-flag-controlled ordered strategy selection and automatic fallback when quote retrieval or execution fails.

Extends MetaMask Pay telemetry by recording quote latency on Across quotes, execution submit latency via the onSubmitted callback, and persisting it to TransactionMeta.metamaskPay.executionLatencyMs; also enriches quote fees with impact/impactRatio (Relay + Across) and hardens feature-flag reads with safe defaults.

Introduces new transaction types perpsAcrossDeposit/predictAcrossDeposit for Across deposits and refactors submission flows (Relay and new Across) to share submitSourceTransactions, including nonce clearing, required-transaction tracking, and intent completion updates.

Written by Cursor Bugbot for commit 4ee01fe. This will update automatically on new commits. Configure here.

@pedronfigueiredo pedronfigueiredo force-pushed the pnf/cor-across-strategy branch 2 times, most recently from 6e9ba0c to 047509a Compare February 2, 2026 15:16
import { getRelayQuotes } from './relay-quotes';
import { submitRelayQuotes } from './relay-submit';

export class RelayProvider implements TokenPayProvider<RelayQuote> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of modularity and risk, can we refactor Relay in a dedicated PR and leave as is here?

Would let us have a working control in the clients when testing also.

/**
* Deposit funds for Across quote.
*/
acrossDeposit = 'acrossDeposit',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually have a pending task to make relayDeposit more granular, so perpsRelayDeposit and predictRelayDeposit, so should do the same for Across also.

totalFiat?: string;

/** Total time spent executing the MetaMask Pay flow, in milliseconds. */
executionLatencyMs?: number;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, alphabetical.


- `tokenPay.providerOrder` controls priority (default: `[primaryProvider, 'relay', 'across']`).
- Each provider can be enabled/disabled via `tokenPay.providers.<id>.enabled`.
- Providers may also implement capability gating in `supports(...)` (e.g., Across rejects same-chain swaps).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like a good mechanism, but can we abstract to PayStrategy in general so we can accommodate fiat in future also and any strategy?

That way, we could isolate this into it's own dedicated PR for risk and easier review?

export enum TransactionPayStrategy {
Bridge = 'bridge',
Relay = 'relay',
TokenPay = 'tokenPay',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was assuming this would just be an abstract internal class to remove duplication.

Ideally the client could specify multiple preferences in priority order, and we pick the first that is supported or throw?

Thereby combining the new fallback mechanism and getStrategy?

continue;
}

if (provider.id === 'across' && !config.providers.across.enabled) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this breaking the abstraction if it knows about the implementations?

return { transactionHash };
}

async function executeSingleQuote(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood the core benefit of the TokenPayStrategy was to remove duplication between the Relay and Across strategies.

But if we're just using it for the fallback logic, would it not be simpler to incorporate that into PayStrategy directly (as mentioned in another comment)?

Then we could create some common token strategy utils to add the origin transactions for example given the duplication?

return Math.ceil(estimatedGas * gasBuffer);
} catch (error) {
log('Gas estimate failed, using fallback', { error });
return 900000;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have some feature flags for this too, so another benefit of a common util.

};
}

function buildDelegationAction(delegation: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all done in the client via a constructor callback, so we can decouple delegations from this controller.

}

const relayer = quote.fees?.relayerTotal?.amountUsd ?? '0';
const app = quote.fees?.app?.amountUsd ?? '0';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried this isn't the total price impact for the user, but just the Across specific fee, does this definitely include the total impact of the bridge provider itself?

Have we confirmed for example, that the quote results in the requested fee minus the this fee only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I adjusted the provider fee to be the impact just like in the case of relay.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

…etrics

- Introduce TokenPay strategy with provider adapter interface and registry
- Add Across provider (quotes + submit) and Relay adapter wrapper
- Implement Across quote normalization (fees, dust, durations, fiat) and actions API payloads for delegated calls
- Add feature flags for tokenPay providers and Across API config
- Add Across submit flow (approvals + swap tx), intent completion, and confirmation waits
- Gate unsupported cases (same-chain, perps deposits) and block type‑4 authorizationList until Across supports it
- Add Across quote latency metrics and execution latency recording in metamaskPay metadata
- Add/extend unit tests for Across quotes/submit/supports and publish hook metrics
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants